﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Td.Additional.Log
{
    /// <summary>
    /// Base logging class.
    /// </summary>
    public abstract class Log : ILog
    {
        #region Properties

        /// <summary>
        /// Gets or sets the log level.
        /// </summary>
        /// <value>The log level.</value>
        public LogLevel LogLevel { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether full qualified method name should be logged.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if full qualified method name should be logged; otherwise, <c>false</c>.
        /// </value>
        public bool LogFullQualifiedMethodName { get; set; }

        #endregion

        #region Private helper

        private void PrepareInformations(LogLevel level, String message)
        {
            if (LogLevel == LogLevel.None || level > LogLevel)
                return;
            System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
            System.Reflection.MethodBase methodBase = null;
            int stackIndex = 2;
            while (stackTrace.FrameCount > stackIndex && typeof(ILog).IsAssignableFrom((methodBase = stackTrace.GetFrame(stackIndex).GetMethod()).ReflectedType))
                stackIndex++;
            string callingMethod = LogFullQualifiedMethodName ? String.Format("{0}.{1}", methodBase.ReflectedType.FullName, methodBase.Name) : methodBase.Name;
            try
            {
                WriteLogEntry(level, callingMethod, message);
            }
            catch (Exception ex)
            {
                Td.Additional.Debug.ConsoleReport("Error writing log entry: {0}", ex.Message);
            }
        }

        #endregion

        #region Virtual members

        /// <summary>
        /// Writes the log entry.
        /// </summary>
        /// <param name="level">The level.</param>
        /// <param name="source">The source.</param>
        /// <param name="message">The message.</param>
        public virtual void WriteLogEntry(LogLevel level, String source, String message)
        {
        }

        #endregion

        #region ILog Member

        /// <summary>
        /// Log the specified message with debug level.
        /// </summary>
        /// <param name="message">The message.</param>
        public void Debug(string message)
        {
            PrepareInformations(LogLevel.Debug, message);
        }

        /// <summary>
        /// Log the specified message with debug level.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="args">The arguments.</param>
        public void Debug(string message, params object[] args)
        {
            PrepareInformations(LogLevel.Debug, 
                String.Format(System.Globalization.CultureInfo.CurrentCulture, message, args));
        }

        /// <summary>
        /// Log the specified message with info level.
        /// </summary>
        /// <param name="message">The message.</param>
        public void Info(string message)
        {
            PrepareInformations(LogLevel.Info, message);
        }

        /// <summary>
        /// Log the specified message with info level.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="args">The arguments.</param>
        public void Info(string message, params object[] args)
        {
            PrepareInformations(LogLevel.Info,
                String.Format(System.Globalization.CultureInfo.CurrentCulture, message, args));
        }

        /// <summary>
        /// Log the specified message with warn level.
        /// </summary>
        /// <param name="message">The message.</param>
        public void Warn(string message)
        {
            PrepareInformations(LogLevel.Warn, message);
        }

        /// <summary>
        /// Log the specified message with warn level.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="args">The arguments.</param>
        public void Warn(string message, params object[] args)
        {
            PrepareInformations(LogLevel.Warn,
              String.Format(System.Globalization.CultureInfo.CurrentCulture, message, args));
        }

        /// <summary>
        /// Log the specified message with error level.
        /// </summary>
        /// <param name="message">The message.</param>
        public void Error(string message)
        {
            PrepareInformations(LogLevel.Error, message);
        }

        /// <summary>
        /// Log the specified message with error level.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="args">The arguments.</param>
        public void Error(string message, params object[] args)
        {
            PrepareInformations(LogLevel.Error,
             String.Format(System.Globalization.CultureInfo.CurrentCulture, message, args));
        }

        /// <summary>
        /// Log the specified message with critical level.
        /// </summary>
        /// <param name="message">The message.</param>
        public void Critical(string message)
        {
            PrepareInformations(LogLevel.Critical, message);
        }

        /// <summary>
        /// Log the specified message with critical level.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="args">The arguments.</param>
        public void Critical(string message, params object[] args)
        {
            PrepareInformations(LogLevel.Critical,
              String.Format(System.Globalization.CultureInfo.CurrentCulture, message, args));
        }

        /// <summary>
        /// Log the specified message with fatal level.
        /// </summary>
        /// <param name="message">The message.</param>
        public void Fatal(string message)
        {
            PrepareInformations(LogLevel.Fatal, message);
        }

        /// <summary>
        /// Log the specified message with fatal level.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="args">The arguments.</param>
        public void Fatal(string message, params object[] args)
        {
            PrepareInformations(LogLevel.Fatal,
              String.Format(System.Globalization.CultureInfo.CurrentCulture, message, args));
        }

        #endregion
    }
}